package org.example;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.beans.BeansException;
import org.example.ApplicationContext;
import org.springframework.beans.factory.ObjectFactory;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.lang.System.in;

/**
 * org.example
 *
 * @author 莫磊
 * @description
 * @date 2025/7/24
 */
@Slf4j
@Data
public class ClassPathXmlApplicationContext implements ApplicationContext {

	/**
	 * spring中的三级缓存机制
	 */
	/**
	 * 一级缓存，使用线程安全的ConcurrentHashMap，在spring中这个容器是给spring中所有的线程共享的，所以需要线程安全
	 */
	private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

	/**
	 * 二级缓存
	 */
	private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

	private Map<String, ObjectFactory<?>> factoryObjects = new ConcurrentHashMap<>();

	/**
	 * 解析配置文件，进行bean的加载
	 *
	 * @param configLocation
	 */
	public ClassPathXmlApplicationContext (String configLocation) {
		try {
			// 解析spring中的xml配置文件，实例化bean，将bean放入spring中的三级缓存中
			// dom4j解析xml的核心对象
			SAXReader reader = new SAXReader();
			// 获取spring的xml配置文件输入流
			InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
			// 读取文件
			Document document = reader.read(in);
			// 获取所有bean标签，该方法没有办法使用，出现classNotFoundException，原因未知
			// List<Node> nodes = document.selectNodes("//bean");
			List<Element> elements = document.getRootElement().elements("bean");
			// 遍历bean标签
			// 向下转型，Element中的方法更丰富
			// 获取bean标签的id值，id为bean标签中有的属性
			// 可以通过反射来对id和className进行对象的创建
			// 通过反射获取对象，进行曝光
			elements.stream().forEach(element -> {
				String s = element.attributeValue("id");
				String className = element.attributeValue("class");
				log.info("bean's id {}, bean's class {}", s, className);
				try {
					Class<?> aClass = Class.forName(className);
					// 获取默认构造方法
					Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
					// 获取实例
					Object o = declaredConstructor.newInstance();
					// 将实例进行曝光
					singletonObjects.put(s, o);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			});

			// 再次便利bean标签，给对象的属性赋值
			elements.forEach(element -> {
				try {
					String id = element.attributeValue("id");
					String className = element.attributeValue("class");
					// 获取这个bean对象
					Class<?> aClass = Class.forName(className);
					// 获取这个bean中的所有property标签
					List<Element> propertys = element.elements("property");
					propertys.forEach(property -> {
						try {
							String name = property.attributeValue("name");
							// 获取属性
							Field field = aClass.getDeclaredField(name);
							String value = property.attributeValue("value");
							String ref = property.attributeValue("ref");
							// 获取属性的setter方法
							Method method = aClass.getMethod("set" + name.substring(0, 1).toUpperCase() + name.substring(1), field.getType());
							if (value != null) {
								Object valueObj = null;
								// 根据属性的类型，将value转换成属性的类型
								valueObj = convertValueType(field, valueObj, value);
								// 获取曝光的对象，使用setter方法将他的属性进行赋值
								method.invoke(singletonObjects.get(id), valueObj);
							}
							if (ref != null) {
								// 获取引用对象，如果引用对象没有在容器中进行管理，那么就创建一个放入
								// Object refObj = singletonObjects.getOrDefault(ref, Class.forName(ref).getDeclaredConstructor().newInstance());
								// 不能使用getOrDefault方法，因为在调用getOrDefault方法时就会先去创建默认值，而默认值有可能时xml中的bean的id值，为非类的全路径，所以就会造成报错
								Object refObj = singletonObjects.getOrDefault(ref, isValidFQCN(ref) ? Class.forName(ref).getDeclaredConstructor().newInstance() : null);
								method.invoke(singletonObjects.get(id), refObj);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					});
				} catch (Exception e) {
					e.printStackTrace();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}



	/** 正则表达式用于匹配完全限定类名 */
	private static final String CLASS_NAME_REGEX = "^([a-zA-Z][0-9]*\\.)*([A-Z][a-zA-Z]*)&";

	/**
	 * 判断是否为类的全路径
	 * @param className
	 * @return
	 */
	public static boolean isValidFQCN(String className) {
		if (className == null || className.isEmpty() || className.startsWith(".") || className.endsWith(".")) {
			return false;
		}
		return className.matches(CLASS_NAME_REGEX);
	}

	private static Object convertValueType (Field field, Object valueObj, String value) throws ParseException {
		if (field.getType().isEnum()) {
			return Enum.valueOf((Class) field.getType(), value);
		}
		switch (field.getType().getSimpleName()) {
			case "int":
				valueObj = Integer.parseInt(value);
				break;
			case "String":
				valueObj = value;
				break;
			case "double":
				valueObj = Double.parseDouble(value);
				break;
			case "Date":
				valueObj = new SimpleDateFormat("yyyy-MM-dd").parse(value);
				break;
			case "boolean":
				valueObj = Boolean.parseBoolean(value);
				break;
			case "long":
				valueObj = Long.parseLong(value);
				break;
			case "Double":
				valueObj = Double.parseDouble(value);
				break;
			case "Integer":
				valueObj = Integer.parseInt(value);
				break;
		}
		return valueObj;
	}

	@Override
	public Object getBean (String name) throws BeansException {
		return singletonObjects.getOrDefault(name, null);
	}

	@Override
	public <T> T getBean (String name, Class<T> requiredType) throws BeansException {
		return (T) getBean(name);
	}
}
